// Copyright (c) Microsoft Corporation.  All rights reserved.

#include "StdAfx.h"
#include "EventRecord.h"

namespace Microsoft { namespace Samples { namespace SqlServer { namespace SSIS { namespace EventLogSource {
	EventRecord::EventRecord() : logRecord(NULL), buffer(NULL) { }

	void EventRecord::Initialize(PEVENTLOGRECORD logRecord) {
		this->logRecord = logRecord;
		this->buffer = reinterpret_cast<PBYTE>(logRecord);

		this->sid = nullptr;
		this->source = nullptr;
		this->computerName = nullptr;
		this->messageStrings = nullptr;
		this->data = nullptr;
	}

	// The category for this event. The meaning of this value depends on the event source. For more information, 
	// see Event Categories.
	UInt16 EventRecord::Category::get() {
		return this->logRecord->EventCategory;
	}

	// The event identifier. The value is specific to the event source for the event, and is used with source name 
	// to locate a description string in the message file for the event source. For more information, see Event Identifiers.
	UInt32 EventRecord::Id::get() {
		return this->logRecord->EventID;
	}

	// The "code" part of the Event Identifier
	UInt16 EventRecord::MessageId::get() {
		return (UInt16)(this->logRecord->EventID & 0x0000FFFF);
	}

	// Type of the event: Error, Information, Warning, ...
	EventType EventRecord::Type::get() { return (EventType)logRecord->EventType; }

	// The event-specific information within this event log record, in bytes. This information could be something specific 
	// (a disk driver might log the number of retries, for example), followed by binary information specific to the event being 
	// logged and to the source that generated the entry.
	array<Byte>^ EventRecord::Data::get() {
		if(this->logRecord->DataLength > 0 && this->data == nullptr) {
			this->data = gcnew array<Byte>(this->logRecord->DataLength);
			pin_ptr<Byte> dataStart = &this->data[0];
			memcpy(dataStart, this->buffer + this->logRecord->DataOffset, this->logRecord->DataLength);
		}

		return this->data;
	}

	// The number of strings present in the log. These strings are 
	// merged into the message before it is displayed to the user.
	// If this member is > 0, there will be strings in the MessageStrings property
	UInt16 EventRecord::NumStrings::get() {
		return this->logRecord->NumStrings;
	}

	// The number of the record within the source event file. This value can be used with the EVENTLOG_SEEK_READ flag in the  
	// ReadEventLog function to begin reading at a specified record. For more information, see Event Log Records.
	// However this managed wrapper does not expose random access to events.
	UInt32 EventRecord::RecordNumber::get() {
		return this->logRecord->RecordNumber;
	}

	// The time at which this entry was submitted. This time is measured in the number of seconds elapsed 
	// since 00:00:00 January 1, 1970, Universal Coordinated Time.
	DateTime EventRecord::TimeGeneratedUTC::get() {
		return DateTime::DateTime(1970, 1, 1).AddSeconds(this->logRecord->TimeGenerated);
	}

	// The time at which this entry was received by the service to be written to the log. This time is 
	// measured in the number of seconds elapsed since 00:00:00 January 1, 1970, Universal Coordinated Time.
	DateTime EventRecord::TimeWrittenUTC::get() {
		return DateTime::DateTime(1970, 1, 1).AddSeconds(this->logRecord->TimeWritten);
	}

	// The SID of the active user at the time this event was logged.  This property may be null.
	SecurityIdentifier^ EventRecord::Sid::get() {
		if(this->sid == nullptr && logRecord->UserSidLength > 0) {
			IntPtr binarySid = (IntPtr)(buffer + logRecord->UserSidOffset);
			this->sid = gcnew SecurityIdentifier(binarySid);
		}

		return this->sid;
	}

	// The description strings within this event log record
	array<String^>^ EventRecord::MessageStrings::get() {
		if(this->NumStrings > 0 && this->messageStrings == nullptr) {
			this->messageStrings = gcnew array<String^>(this->NumStrings);
			int offset = this->logRecord->StringOffset;
			for(int i = 0; i < this->NumStrings; i++) {
				this->messageStrings[i] = gcnew String(reinterpret_cast<PWSTR>(this->buffer + offset));
				offset += (this->messageStrings[i]->Length + 1) * sizeof(WCHAR);
			}
		}

		return this->messageStrings;
	}

	// The name of the computer that generated the event
	String^ EventRecord::ComputerName::get() {
		if(this->computerName == nullptr) {
			this->computerName = gcnew String(reinterpret_cast<PWSTR>(this->buffer + sizeof(EVENTLOGRECORD)) + this->Source->Length + 1);
		}

		return this->computerName;
	}

	// The source name is a string that specifies the name of the event source. 
	String^ EventRecord::Source::get() {
		if(this->source == nullptr) {
			this->source = gcnew String(reinterpret_cast<PWSTR>(this->buffer + sizeof(EVENTLOGRECORD)));
		}

		return this->source;
	}

	String^ EventRecord::ToString() {
		StringBuilder^ retVal = gcnew StringBuilder();

		retVal->AppendFormat("{0}\t{1}\t", Enum::GetName(EventType::typeid, this->Type), this->TimeGeneratedUTC);
		retVal->AppendFormat("{0}\t{1}\t", this->Source, this->Category);
		retVal->AppendFormat("{0}\t{1}\t{2}", this->MessageId, this->Sid, this->ComputerName);

		return retVal->ToString();
	}
} } } } }